﻿/************************ (C) COPYLEFT 2018 Merafour *************************
* File Name          : Ini.c
* Author             : Merafour
* Last Modified Date : 04/05/2020
* Description        : 配置文件解析.
********************************************************************************
* https://merafour.blog.163.com
* merafour@163.com
* https://github.com/merafour
******************************************************************************/
#include "Ini.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <assert.h>
//#include <errno.h>
//#include <ctype.h>
#include <stdarg.h>

//enum keylen{ KEYVALLEN = 256};
//#define CFGPATH "./cfg.data"

/***
Ref:https://www.runoob.com/cprogramming/c-function-isspace.html
C 库函数 int isspace(int c) 检查所传的字符是否是空白字符。
标准的空白字符包括：
' '     (0x20)    space (SPC) 空格符
'\t'    (0x09)    horizontal tab (TAB) 水平制表符    
'\n'    (0x0a)    newline (LF) 换行符
'\v'    (0x0b)    vertical tab (VT) 垂直制表符
'\f'    (0x0c)    feed (FF) 换页符
'\r'    (0x0d)    carriage return (CR) 回车符
**/
static int _isspace(const char c)
{
	if(' '==c)  return 1; // (0x20)    space (SPC) 空格符
	if('\t'==c) return 1; // (0x09)    horizontal tab (TAB) 水平制表符    
	if('\n'==c) return 1; // (0x0a)    newline (LF) 换行符
	if('\v'==c) return 1; // (0x0b)    vertical tab (VT) 垂直制表符
	if('\f'==c) return 1; // (0x0c)    feed (FF) 换页符
	if('\r'==c) return 1; // (0x0d)    carriage return (CR) 回车符
	return 0;
}

/*   删除左边的空格   */
static char * l_trim(char * szOutput, const char *szInput)
{
//    assert(szInput != NULL);
//    assert(szOutput != NULL);
//    assert(szOutput != szInput);
    for(; (*szInput != '\0') && (_isspace(*szInput)); ++szInput){
        ;
    }
    return strcpy(szOutput, szInput);
}

/*   删除右边的空格   */
static char *r_trim(char *szOutput, const char *szInput)
{
    char *p = NULL;
//    assert(szInput != NULL);
//    assert(szOutput != NULL);
//    assert(szOutput != szInput);
    strcpy(szOutput, szInput);
    for(p = szOutput + strlen(szOutput) - 1; (p >= szOutput) && (_isspace(*p)); --p){
        ;
    }
    *(++p) = '\0';
    return szOutput;
}

/*   删除两边的空格   */
static char * a_trim(char * szOutput, const char * szInput)
{
    char *p = NULL;
//    assert(szInput != NULL);
//    assert(szOutput != NULL);
    l_trim(szOutput, szInput);
    for(p = szOutput + strlen(szOutput) - 1;(p >= szOutput) && (_isspace(*p)); --p){
        ;
    }
    *(++p) = '\0';
    return szOutput;
}
/*   获取一行   */
static char *serach_gets(struct ini_Parse* const Ini, char * const str, const int n)
{
    int _end;
    int count=0;
    char _byte=0;
    _end = Ini->pos + n;
    memset(str, 0, (size_t)n);
    for(count=0; count<n-1; count++)
    {
        if(Ini->pos>=Ini->tlen) return  NULL;
        if(Ini->pos>=_end) return  NULL;
        _byte = Ini->text[Ini->pos++];
        str[count] = _byte;
        if('\n'==_byte) break;
        if('\0'==_byte) break;
    }
    //str[count] = '\0';
    str[n-1] = '\0';
    /*if(0==count)
    {
        printf("[%s-%d] stream->pos:%d stream->_stext:%d _end:%d _byte:0x%X\n", __func__, __LINE__, stream->pos, stream->_stext, _end, _byte);
        return  NULL;
    }*/
    return  str;
}
// 跳到下一行
static int next_gets(struct ini_Parse* const Ini, const int _seek)
{
    char _byte=0;
    int _pos;                 // 当前文件指针位置
    for(_pos=_seek; _pos<Ini->tsize; _pos++)
    {
        _byte = Ini->text[_pos];
        if('\n'==_byte)
        {
            _pos++;   // skip  '\n'
            break;
        }
        if('\0'==_byte) break;
    }
    return  _pos;
}
// 跳到前一行
static int previ_gets(struct ini_Parse* const Ini, const int _seek)
{
    char _byte=0;
    int _pos=0;                 // 当前文件指针位置
    for(_pos=_seek; _pos>=0; _pos--)
    {
        _byte = Ini->text[_pos];
        if('\n'==_byte)
        {
            _pos++;   // skip  '\n'
            break;
        }
        if('\0'==_byte)
        {
            _pos++;   // skip  '\0'
            break;
        }
    }
    return  _pos;
}
/*   在当前位置插入一行数据   */
// const int tsize;        // buf 大小
static int _insert(struct ini_Parse* const Ini, const int seek, const char item[])
{
    int _size;
    int i;
    // move item
    _size = (int)strlen(item) + 2; // "\r\n"
    //printf("[%s-%d] Item[%d]:%s", __func__, __LINE__, _size, buf);
    // 所有数据向后移动 _size byte
    for(i=Ini->tsize-1; i>=(seek+_size); i--)
    {
        Ini->text[i] = Ini->text[i-_size];
    }
    // insert curr pos
    memcpy(&Ini->text[seek], item, _size);
    Ini->text[seek+_size-2] = '\r';
    Ini->text[seek+_size-1] = '\n';
    Ini->tlen = (int)strlen(Ini->text);
    return 0;
}
/*   格式化插入一行数据   */
static int insert(struct ini_Parse* const Ini, const int seek, const char *__format, ...)
{
    char buf[256];
    //int _size;
    //int i;
    va_list ap;
    memset(buf, 0, sizeof(buf));
    va_start(ap, __format);
    //vprintf(__format, ap);
    //snprintf(prt, __n-_size, __format, ap);
    vsnprintf(buf, sizeof(buf), __format, ap);
    va_end(ap);
#if 0
    // move item
    _size = strlen(buf);
    //printf("[%s-%d] Item[%d]:%s", __func__, __LINE__, _size, buf);
    // 所有数据向后移动 _size byte
    for(i=Size()-1; i>=(seek+_size); i--)
    {
        Info[i] = Info[i-_size];
    }
    // insert curr pos
    memcpy(&Info[seek], buf, _size);
    _isize = strlen(Info);
#endif
    _insert(Ini, seek, buf);
    return 0;
}
/*   获取一段信息,带默认值   */
int ini_get_field(struct ini_Parse* const Ini, const char section[], const char _key[], const char dft[], char _value[])
{
    int dft_size = (int)strlen(dft);
    //printf("[%s-%d] \n", __func__, __LINE__);
    int ret = ini_get_section_key_value(Ini, section, _key, _value);
    if(0!=ret)
    {
        memcpy(_value, dft, dft_size);
        _value[dft_size] = '\0';
    }
    return 0;
}
/*   初始化样例   */
int ini_list_samples(struct ini_Parse* const Ini)
{
    // 清理缓存
    memset(Ini->text, 0x00, Ini->tsize);
    ini_print(Ini, "# Ini 配置文件 \r\n");
    ini_print(Ini, "# '#' 后面的内容为注释 \r\n");
    ini_print(Ini, "# Version:	%s \r\n", INI_FW_VER);
    ini_print(Ini, "# [EG1] 表示 EG1 段的开始,到下一个段之前结束 \r\n");
    ini_print(Ini, "# Key=Val 表示键值对,且键值对必须在段里面否则不解析 \r\n");
    ini_print(Ini, "# 以下为示例\r\n");
    ini_print(Ini, "# \r\n");
    ini_print(Ini, "# 日志选项 \r\n");
    ini_print(Ini, "# no:关闭,yes:开启 \r\n");
    //ini_print(Ini, "[LOG] \r\n");
    //ini_print(Ini, "TurnOn=no \r\n");
    //ini_print(Ini, "[End] \r\n");
    ini_print(Ini, "# 文件结束 \r\n");
    //ini_update(Ini, "Test", "Version", INI_FW_VER);
    ini_update_item(Ini, "LOG", "TurnOn", "%s=\"%s\" ", "TurnOn", "no");
    ini_update_item(Ini, "AppName", "KeyName", "%s=\"%s\" ", "KeyName", "0x12345678");
    return 0;
}
// 构造一个空 Ini数据结构
int ini_list_empty(struct ini_Parse* const Ini)
{
    // 清理缓存
    memset(Ini->text, 0x00, Ini->tsize);
    ini_print(Ini, "#\r\n");
    // Ini结束
    ini_print(Ini, "[End]\r\n");
    return 0;
}

/*   获取一个段的地址   */
int ini_get_section(struct ini_Parse* const Ini, const char AppName[], int* const _pos)
{
    char appname[32];
    char *buf;//,*c;
    char buf_i[INI_ITEM_LEN], buf_o[INI_ITEM_LEN];
    int found=0; /* 1 AppName 2 KeyName */
    memset( appname, 0, sizeof(appname) );
    snprintf( appname, sizeof(appname)-1,"[%s]", AppName );

    Ini->pos = 0;
    //printf("[%s-%d] pos:%d size:%d \n", __func__, __LINE__, pos, size);
    while( (Ini->pos<Ini->tlen) && (Ini->pos<Ini->tsize) && serach_gets(Ini, buf_i, INI_ITEM_LEN)!=NULL )
    {
        // 读取一行
        //printf("[%s-%d] pos:%d size:%d buf_i:%s \n", __func__, __LINE__, pos, size, buf_i);
        l_trim(buf_o, buf_i);
        if( strlen(buf_o) <= 0 )
            continue;
        buf = NULL;
        buf = buf_o;

        //printf("[%s-%d] appname:%s fp.pos:%d found:%d buf:%s\n", __func__, __LINE__, appname, fp.pos, found, buf);
        //if( found == 0 )
        //{
            if( buf[0] != '[' )
            {
                continue;
            }
            else if ( strncmp(buf,appname,strlen(appname))==0 )
            {
                found = 1;
                //printf("\r\n\r\n[%s-%d] text[%d]:\n%s\n", __func__, __LINE__, pos, &text[pos]);
                //continue;
                break;
            }
        //}
        /*else if( found == 1 )
        {
            if('#'==buf[0]) // if( buf[0] == '#' )
            {
                continue;
            }
            //printf("\r\n[%s-%d] text[%d]:\n%s\n", __func__, __LINE__, pos, &text[pos]);
            break;
        }*/
    }
    //printf("[%s-%d] \n", __func__, __LINE__);
    *_pos = Ini->pos;
    if( found == 1 ) return 0;
    else return -1;
    //return pos;
}
/*   获取一个键值对的地址   */
int ini_get_section_key(struct ini_Parse* const Ini, const char AppName[], const char KeyName[], int* const _pos)
{
    char appname[32],keyname[32];
    char *buf,*c;
    char buf_i[INI_ITEM_LEN], buf_o[INI_ITEM_LEN];
    int found=0; /* 1 AppName 2 KeyName */
    memset( appname, 0, sizeof(appname) );
    sprintf( appname,"[%s]", AppName );

    Ini->pos = 0;
    //printf("[%s-%d] pos:%d size:%d \n", __func__, __LINE__, pos, size);
    while( (Ini->pos<Ini->tlen) && (Ini->pos<Ini->tsize) && (Ini->pos<Ini->tsize) && serach_gets(Ini, buf_i, INI_ITEM_LEN)!=NULL )
    {
        // 读取一行
        //printf("[%s-%d] pos:%d size:%d buf_i:%s \n", __func__, __LINE__, pos, size, buf_i);
        l_trim(buf_o, buf_i);
        if( strlen(buf_o) <= 0 )
            continue;
        buf = NULL;
        buf = buf_o;

        //printf("[%s-%d] appname:%s fp.pos:%d found:%d buf:%s\n", __func__, __LINE__, appname, fp.pos, found, buf);
        if( found == 0 )
        {
            if( buf[0] != '[' )
            {
                continue;
            }
            else if ( strncmp(buf,appname,strlen(appname))==0 )
            {
                found = 1;
                continue;
            }
        }
        else if( found == 1 )
        {
            if('#'==buf[0]) // if( buf[0] == '#' )
            {
                continue;
            }
            // add
            //else if(0!=is_empty_line(buf, KEYVALLEN)) continue;
            else if ( buf[0] == '[' ) // 遇到下一个段,结束
            {
                break;
            }
            else
            {
                // 第一个'='前面的字符串为键值对的键
                if( (c = (char*)strchr(buf, '=')) == NULL )
                    continue;
                memset( keyname, 0, sizeof(keyname) );

                //sscanf( buf, "%[^=|^ |^\t]", keyname );
                *c = '\0';
                strcpy(keyname, buf);
                if( strcmp(keyname, KeyName) == 0 )
                {
                    found = 2;
                    break;
                }
                else
                {
                    continue;
                }
            }
        }
    }
    *_pos = Ini->pos;
    //return pos;
    if( found == 2 ) return 0;
    else return -1;
}
/*   获取一个 Key对应的值   */
int ini_get_section_key_value(struct ini_Parse* const Ini, const char AppName[], const char KeyName[], char KeyVal[])
{
    int _pos;
    char buf_i[INI_ITEM_LEN], buf_o[INI_ITEM_LEN];
    if(0==ini_get_section_key(Ini, AppName, KeyName, &_pos))
    {
        // 往回检索一行必须先回退换行符
        _pos = previ_gets(Ini, _pos-2); //"\r\n"
        // previ_gets函数不会重置 Ini->pos
        Ini->pos = _pos;
        //printf("[%s--%d] Key:%s \r\n", __func__, __LINE__, &Ini->text[_pos]);
        serach_gets(Ini, buf_i, INI_ITEM_LEN);
        // 删除两边空格
        a_trim(buf_o, buf_i);
        //printf("[%s--%d] buf_i:%s \r\n", __func__, __LINE__, buf_i);
        //printf("[%s--%d] buf_o:%s \r\n", __func__, __LINE__, buf_o);
        // 拷贝键值对的值
        strcpy(KeyVal, &buf_o[strlen(KeyName)+1]);
        return 0;
    }
    //printf("[%s--%d] \r\n", __func__, __LINE__);
    return -1;
}
/*   更新一个键值对，没有则创建   */
int ini_update_item(struct ini_Parse* const Ini, const char AppName[], const char KeyName[], const char *__format, ...)
{
    int _seek;
    char buf[256];
    va_list ap;
    memset(buf, 0, sizeof(buf));
    va_start(ap, __format);
    //vprintf(__format, ap);
    //snprintf(prt, __n-_size, __format, ap);
    //vsprintf(buf, __format, ap);
    if(NULL!=KeyName)
    {
        int len;
        len = strlen(KeyName);
        if(len>=128) // Key长度限制
        {
            va_end(ap);
            return -1;
        }
        memcpy(buf, KeyName, len);
        buf[len] = '=';
        // "\r\n" ==> '\r' '\n' '\0'
        vsnprintf(&buf[len+1], sizeof(buf)-3-len-1, __format, ap);
    }
    else vsnprintf(buf, sizeof(buf)-1, __format, ap);
    va_end(ap);
    //printf("[%s-%d] AppName[%s] KeyName[%s] _seek[%d] _isize[%d] \n", __func__, __LINE__, AppName, KeyName, _seek, Ini->tlen);
    if(0!=ini_get_section(Ini, AppName, &_seek))  // 添加新的段
    {
        // "End"为显性结束符
        //if(0!=ini_get_section(Ini, "End", &_seek)) return -2;
        if(0!=ini_get_section(Ini, "End", &_seek))
        {
            //return -2;
            _seek = Ini->tlen;
        }
        _seek = previ_gets(Ini, _seek-3);  // skip curr line
#if 0
        insert(Ini, _seek, "\r\n[%s]\r\n", AppName);  // 在文件末尾插入
        _seek = next_gets(Ini, _seek+3);
        _insert(Ini, _seek, buf);
#else
        insert(Ini, _seek, "[%s]\r\n%s", AppName, buf);  // 在文件末尾插入
#endif
    }
    else
    {
        if(0==ini_get_section_key(Ini, AppName, KeyName, &_seek)) // found
        {
            _seek = previ_gets(Ini, _seek-3);  // skip curr line
            //printf("[%s-%d] AppName[%s] KeyName[%s] _seek[%d]\n", __func__, __LINE__, AppName, KeyName, _seek);
            ini_del_item(Ini, _seek);
            _insert(Ini, _seek, buf);
        }
        else // new
        {
            if(0!=ini_get_section(Ini, AppName, &_seek)) return -2;
            _insert(Ini, _seek, buf);
        }
    }
    //printf("[%s-%d]\r\n", __func__, __LINE__);
    return 0;
}
/*   删除一个键值对   */
int ini_del(struct ini_Parse* const Ini, const char AppName[], const char KeyName[])
{
    int _seek;
    // 添加新的段
    if(0!=ini_get_section(Ini, AppName, &_seek)) return 0;
    if(0==ini_get_section_key(Ini, AppName, KeyName, &_seek)) // found
    {
        _seek = previ_gets(Ini, _seek-3);  // skip curr line
        //printf("[%s-%d] AppName[%s] KeyName[%s] _seek[%d]\n", __func__, __LINE__, AppName, KeyName, _seek);
        ini_del_item(Ini, _seek);
    }
    //printf("[%s-%d]\r\n", __func__, __LINE__);
    return 0;
}
/*   删除一行数据   */
int ini_del_item(struct ini_Parse* const Ini, const int seek)
{
    int i;
    int _pos = next_gets(Ini, seek);
    int _size = _pos - seek;
    // 所有数据向前移动 _size byte
    for(i=seek; i<(Ini->tsize-_size); i++)
    {
        Ini->text[i] = Ini->text[i+_size];
    }
    for(i=Ini->tsize-_size; i<Ini->tsize; i++)
    {
        Ini->text[i] = '\0';
    }
    //printf("[%s-%d] del_item[%d]:\r\n%s", __func__, __LINE__, _size, &Info[seek]);
    return 0;
}
/*   输出一行信息到 Ini   */
int ini_print(struct ini_Parse* const Ini, const char *__format, ...)
{
    char* prt=NULL;
    const size_t __n = Ini->tsize;
    size_t _size=0;
    va_list ap;
    _size = strlen(Ini->text);
    if(_size>=__n) return -1;
    prt = &Ini->text[_size];
    va_start(ap, __format);
    //vprintf(__format, ap);
    //snprintf(prt, __n-_size, __format, ap);
    vsnprintf(prt, __n-_size, __format, ap);
    va_end(ap);
    Ini->tlen = (int)strlen(Ini->text);
    return 0;
}

